7.1: AsyncTask dan AsyncTaskLoader
Materi:
- Thread UI
- AsyncTask
- Penggunaan AsyncTask
- Contoh AsyncTask
- Mengeksekusi AsyncTask
- Membatalkan AsyncTask
- Keterbatasan AsyncTask
- Loader
- AsyncTaskLoader
- Penggunaan AsyncTaskLoader
- Praktik terkait
- Ketahui selengkapnya
Ada dua cara untuk melakukan pemrosesan latar belakang di Android: menggunakan kelas AsyncTask
, atau menggunakan kerangka kerja Loader
, yang menyertakan kelas AsyncTaskLoader
yang menggunakan AsyncTask
. Di sebagian besar situasi, Anda akan memilih kerangka kerja Loader
, namun penting untuk mengetahui cara kerja AsyncTask
, sehingga Anda bisa membuat pilihan yang bagus.
Dalam bab ini Anda akan mempelajari alasan pentingnya memproses beberapa tugas di latar belakang, di luar thread UI. Anda akan mempelajari cara menggunakan AsyncTask
, bila tidak menggunakan AsyncTask
, dan dasar-dasar penggunaan loader.
Thread UI
Bila aplikasi Android dimulai, aplikasi membuat thread utama, yang sering disebut thread UI. Thread UI mengirimkan kejadian ke widget antarmuka pengguna (UI) yang sesuai, dan ini merupakan tempat aplikasi Anda berinteraksi dengan komponen dari toolkit UI Android (komponen dari paket android.widget dan android.view).
Model thread Android memiliki dua aturan:
1. Jangan memblokir thread UI.
Thread UI perlu memberikan perhatiannya untuk menggambar UI dan menjaga aplikasi tetap responsif terhadap masukan pengguna. Jika semuanya terjadi di thread UI, operasi panjang seperti akses jaringan atau kueri database bisa memblokir seluruh UI. Dari perspektif pengguna, aplikasi tersebut akan mogok. Lebih buruk lagi, jika thread UI diblokir selama lebih dari beberapa detik (saat ini sekitar 5 detik) pengguna akan ditampilkan dialog "application not responding" (ANR). Pengguna bisa memutuskan untuk keluar dari aplikasi dan mencopot pemasangannya.
Untuk memastikan aplikasi Anda tidak memblokir thread UI:
- Selesaikan semua pekerjaan dalam waktu kurang dari 16 md untuk setiap layar UI.
- Jangan menjalankan tugas asinkron dan tugas lain yang berjalan lama pada thread UI. Sebagai gantinya, implementasikan tugas pada thread latar belakang menggunakan
AsyncTask
(untuk tugas singkat atau yang bisa disela) atauAsyncTaskLoader
(untuk tugas berprioritas tinggi, atau tugas yang perlu melaporkan kembali ke pengguna atau UI). 2. Lakukan pekerjaan UI hanya pada thread UI.
Jangan menggunakan thread latar belakang untuk memanipulasi UI Anda, karena toolkit UI Android bukan thread-safe.
AsyncTask
Gunakan kelas AsyncTask
untuk mengimplementasikan tugas asinkron yang berjalan lama di thread pekerja. (Thread pekerja adalah thread yang bukan thread utama atau thread UI.) AsyncTask
memungkinkan Anda menjalankan operasi latar belakang dan mempublikasikan hasil di thread UI tanpa memanipulasi thread atau penangan.
Bila AsyncTask
dieksekusi, maka akan melalui empat langkah:
onPreExecute()
dipanggil di thread UI sebelum tugas dieksekusi. Langkah ini biasanya digunakan untuk mempersiapkan tugas, misalnya dengan menampilkan bilah kemajuan di UI.doInBackground(Params...)
dipanggil pada thread latar belakang segera setelahonPreExecute()
selesai. Langkah ini menjalankan komputasi latar belakang, mengembalikan hasil, dan meneruskan hasilnya keonPostExecute()
. MetodedoInBackground()
juga bisa memanggilpublishProgress(Progress...)
untuk mempublikasikan satu atau beberapa unit kemajuan.onProgressUpdate(Progress...)
berjalan di thread UI setelahpublishProgress(Progress...)
dipanggil. GunakanonProgressUpdate()
untuk melaporkan suatu bentuk kemajuan ke thread UI sewaktu komputasi latar belakang dieksekusi. Misalnya, Anda bisa menggunakannya untuk meneruskan data guna menganimasikan bilah kemajuan atau menampilkan log di bidang teks.onPostExecute(Result)
berjalan di thread UI setelah komputasi latar belakang selesai.
Untuk detail selengkapnya mengenai metode ini, lihat referensi AsyncTask
. Di bawah ini adalah diagram urutan pemanggilan.
Penggunaan AsyncTask
Untuk menggunakan kelas AsyncTask
, definisikan subkelas AsyncTask
yang menggantikan metode doInBackground(Params...)
(dan biasanya juga metode onPostExecute(Result)
). Bagian ini menjelaskan parameter dan penggunaan AsyncTask
, kemudian menampilkan contoh lengkap.
Parameter AsyncTask
Di subkelas AsyncTask
, sediakan tipe data untuk tiga jenis parameter.
- "Params" menetapkan tipe parameter yang diteruskan ke
doInBackground()
sebagai larik. - "Progress" menetapkan tipe parameter yang diteruskan ke
publishProgress()
di thread latar belakang. Parameter ini selanjutnya diteruskan ke metodeonProgressUpdate()
di thread utama. - "Result" menetapkan tipe parameter yang dikembalikan
doInBackground()
. Parameter ini secara otomatis diteruskan keonPostExecute()
di thread utama.
Tetapkan tipe data untuk setiap tipe parameter ini, atau gunakan Void
jika tipe parameter tidak akan digunakan. Misalnya:
public class MyAsyncTask extends AsyncTask <String, Void, Bitmap>{}
Dalam deklarasi kelas ini:
- Tipe parameter "Params" adalah
String
, yang berarti bahwaMyAsyncTask
memerlukan satu atau beberapa string sebagai parameter didoInBackground()
, misalnya untuk digunakan di kueri. - Tipe parameter "Progress" adalah
Void
, yang berarti bahwaMyAsyncTask
tidak akan menggunakan metodepublishProgress()
atauonProgressUpdate()
. - Tipe parameter "Result" adalah
Bitmap
.MyAsyncTask
mengembalikan Bitmap didoInbackground()
, yang diteruskan ke dalamonPostExecute()
.
Contoh AsyncTask
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
Contoh di atas melewati tiga dari empat langkah-langkah AsyncTask dasar:
doInBackground()
mengunduh materi, tugas yang berjalan lama. Langkah ini menghitung persentase file yang diunduh dari indeks loopfor
dan meneruskannya kepublishProgress()
. Pemeriksaan untukisCancelled()
di dalam loopfor
memastikan bahwa tugas telah dibatalkan, sistem tidak menunggu hingga loop selesai.onProgressUpdate()
memperbarui kemajuan persentase. Ini dipanggil setiap kali metodepublishProgress()
dipanggil di dalamdoInBackground()
, yang memperbarui kemajuan persentase.doInBackground()
menghitung jumlah total byte yang diunduh dan mengembalikannya.onPostExecute()
menerima hasil yang dikembalikan dan meneruskannya ke dalamonPostExecute()
, yang ditampilkan di dialog.
Tipe parameter yang digunakan dalam contoh ini adalah:
URL
untuk tipe parameter "Params". TipeURL
berarti Anda bisa meneruskan sejumlah URL ke dalam panggilan, dan URL secara otomatis diteruskan ke dalam metodedoInBackground()
sebagai larik.Integer
untuk tipe parameter "Progress".Long
untuk tipe parameter "Result".
Mengeksekusi AsyncTask
Setelah Anda mendefinisikan subkelas AsyncTask
, buat instance-nya di thread UI. Kemudian panggil execute()
di instance, dengan meneruskan sejumlah parameter. (Parameter tersebut sesuai dengan tipe parameter "Params" yang dibahas di atas).
Misalnya, untuk mengeksekusi tugas DownloadFilesTask yang didefinisikan di atas, gunakan baris kode berikut:
new DownloadFilesTask().execute(url1, url2, url3);
Membatalkan AsyncTask
Anda bisa membatalkan tugas kapan saja, dari thread apa pun, dengan memanggil metode cancel()
.
- Metode
cancel()
akan mengembalikanfalse
jika tugas tidak bisa dibatalkan, biasanya karena sudah diselesaikan secara normal. Jika tidak,cancel()
akan mengembalikantrue
. - Untuk mengetahui apakah tugas sudah dibatalkan, periksa nilai yang dikembalikan
isCancelled()
secara berkala daridoInBackground(Object[])
, misalnya dari dalam loop seperti yang ditampilkan dalam contoh di atas. MetodeisCancelled()
akan mengembalikantrue
jika tugas dibatalkan sebelum diselesaikan secara normal. - Setelah tugas
AsyncTask
dibatalkan,onPostExecute()
tidak akan digunakan setelahdoInBackground()
dikembalikan. Sebagai gantinya,onCancelled(Object)
akan dipanggil. Implementasi defaultonCancelled(Object)
cukup memanggilonCancelled()
dan mengabaikan hasil. - Secara default, tugas yang sedang diproses boleh diselesaikan. Untuk memperbolehkan
cancel()
menyela thread yang sedang mengeksekusi tugas, teruskantrue
untuk nilaimayInterruptIfRunning
.
Keterbatasan AsyncTask
AsyncTask
tidak praktis untuk beberapa kasus penggunaan:
Perubahan pada konfigurasi perangkat menyebabkan masalah.
Bila konfigurasi perangkat berubah sewaktu
AsyncTask
berjalan, misalnya jika pengguna mengubah orientasi layar, aktivitas yang membuatAsyncTask
akan dimusnahkan dan dibuat ulang. MetodeAsyncTask
tidak dapat mengakses aktivitas yang baru saja dibuat dan hasilAsyncTask
tidak akan dipublikasikan.Objek
AsyncTask
lama tetap ada, dan aplikasi Anda bisa kehabisan memori atau mogok.Jika aktivitas yang membuat
AsyncTask
dimusnahkan,AsyncTask
tidak akan dimusnahkan bersamanya. Misalnya, jika pengguna keluar dari aplikasi setelahAsyncTask
dimulai,AsyncTask
akan terus menggunakan sumber daya kecuali jika Anda memanggilcancel()
.
Bila menggunakan AsyncTask
:
- Tugas singkat atau yang bisa disela.
- Tugas yang tidak perlu untuk melaporkan kembali ke UI atau pengguna.
- Tugas dengan prioritas rendah yang bisa ditinggalkan sebelum selesai.
Untuk semua situasi lainnya, gunakan AsyncTaskLoader
, adalah bagian dari kerangka kerja Loader
yang akan dijelaskan berikutnya.
Loader
Tugas latar belakang biasanya digunakan untuk memuat data seperti laporan prakiraan cuaca atau ulasan film. Pemuatan data bisa jadi banyak menggunakan memori, dan Anda ingin data tersedia sekalipun jika konfigurasi perangkat berubah. Untuk situasi ini, gunakan loader, yang berupa rangkaian kelas yang memfasilitasi pemuatan data ke dalam aktivitas.
Loader menggunakan kelas LoaderManager
untuk mengelola satu atau beberapa loader. LoaderManager
menyertakan serangkaian callback bila loader telah dibuat, bila pemuatan datanya selesai, dan bila disetel ulang.
Memulai loader
Gunakan kelas LoaderManager
untuk mengelola satu atau beberapa instance Loader
dalam aktivitas atau fragmen. Gunakan initLoader()
untuk melakukan inisialisasi dan mengaktifkannya. Biasanya, Anda melakukan ini dalam metode onCreate()
aktivitas. Misalnya:
// Prepare the loader. Either reconnect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);
Jika Anda menggunakan Pustaka Dukungan, buat panggilan ini menggunakan getSupportLoaderManager()
sebagai ganti getLoaderManager()
. Misalnya:
getSupportLoaderManager().initLoader(0, null, this);
Metode initLoader()
memerlukan tiga parameter:
- ID unik yang mengidentifikasi loader. ID ini bisa berupa apa saja yang Anda inginkan.
- Argumen opsional yang disediakan ke loader saat pembuatan, dalam bentuk
Bundle
. Jika loader sudah ada, parameter ini akan diabaikan. - Implementasi
LoaderCallbacks
, yang dipanggil olehLoaderManager
untuk melaporkan kejadian loader. Dalam contoh ini, kelas lokal mengimplementasikan antarmukaLoaderManager.LoaderCallbacks
, sehingga meneruskan referensi ke dirinya sendiri,this
.
Panggilan initLoader()
memiliki dua kemungkinan hasil:
- Jika loader yang ditetapkan melalui ID sudah ada, maka loader yang dibuat terakhir menggunakan ID itu akan digunakan kembali.
Jika loader yang ditetapkan melalui ID tidak ada,
initLoader()
akan memicu metodeonCreateLoader).
Di sinilah Anda mengimplementasikan kode untuk membuat instance dan mengembalikan loader baru.Catatan:BilainitLoader()
membuat loader atau menggunakan kembali loader yang ada, implementasiLoaderCallbacks
yang diberikan akan dikaitkan dengan loader dan dipanggil bila keadaan loader berubah. Jika loader yang diminta sudah ada dan sudah menghasilkan data, maka sistem segera memanggilonLoadFinished()
(selamainitLoader()
), jadi bersiaplah jika hal ini terjadi.
Masukkan panggilan keinitLoader()
dionCreate()
sehingga aktivitas bisa dihubungkan kembali ke loader yang sama bila konfigurasi berubah. Dengan cara itu, loader tidak kehilangan data yang sudah dimuatnya.
Memulai ulang loader
Bila initLoader()
menggunakan kembali loader yang ada, maka data yang telah dimuat loader tidak akan diganti, namun kadang-kadang Anda perlu menggantinya. Misalnya, bila Anda menggunakan kueri pengguna untuk melakukan penelusuran dan pengguna memasukkan kueri baru, Anda perlu memuat ulang data dengan menggunakan istilah penelusuran baru. Dalam situasi ini, gunakan metode restartLoader()
dan teruskan ID loader yang ingin dimulai ulang. Hal ini akan memaksa muatan data lain dengan data masukan baru.
Tentang metode restartLoader()
:
restartLoader()
menggunakan argumen yang sama denganinitLoader()
.restartLoader()
akan memicu metodeonCreateLoader()
, seperti yang dilakukaninitLoader()
saat membuat loader baru.- Jika sudah ada loader dengan ID yang diberikan,
restartLoader()
akan memulai ulang loader yang diidentifikasi dan mengganti datanya. - Jika tidak ada loader dengan ID yang diberikan,
restartLoader()
akan memulai loader baru.
Callback LoaderManager
Objek LoaderManager
secara otomatis memanggil onStartLoading()
saat membuat loader. Setelah itu, LoaderManager
akan mengelola keadaan loader berdasarkan pada keadaan aktivitas dan data, misalnya dengan memanggil onLoadFinished()
bila data telah dimuat.
Untuk berinteraksi dengan loader, gunakan salah satu callback LoaderManager
di aktivitas yang memerlukan data:
- Panggil
onCreateLoader()
agar bisa membuat instance dan mengembalikan loader baru untuk ID yang diberikan. - Panggil
onLoadFinished()
bila loader yang dibuat sebelumnya selesai memuat. Di sinilah Anda biasanya ingin memindahkan data ke dalam tampilan aktivitas. - Panggil
onLoaderReset()
bila loader yang dibuat sebelumnya sedang disetel ulang, sehingga datanya tidak tersedia. Di sinilah aplikasi harus membuang semua referensi apa pun yang dimilikinya ke data loader.
Subkelas Loader
bertanggung jawab atas pemuatan data sebenarnya. Subkelas Loader
yang Anda gunakan bergantung pada tipe data yang dimuat, namun salah satu yang paling mudah adalah AsyncTaskLoader
, yang akan dijelaskan berikutnya. AsyncTaskLoader
menggunakan AsyncTask
untuk menjalankan tugas pada thread pekerja.
AsyncTaskLoader
AsyncTaskLoader
adalah loader yang setara dengan AsyncTask
. AsyncTaskLoader
menyediakan metode, loadInBackground()
, yang dijalankan di thread terpisah. Hasil loadInBackground()
secara otomatis dikirimkan ke thread UI, melalui onLoadFinished()
LoaderManager
callback.
Penggunaan AsyncTaskLoader
Untuk mendefinisikan subkelas AsyncTaskLoader
, buat kelas yang memperluas AsyncTaskLoader<D>
, dalam hal ini D
adalah tipe data yang sedang Anda muat. Misalnya, AsyncTaskLoader
ini akan memuat daftar string:
public static class StringListLoader extends AsyncTaskLoader<List<String>> {}
Berikutnya, implementasikan konstruktor yang cocok dengan implementasi super kelas:
- Konstruktor menggunakan konteks aplikasi sebagai argumen dan meneruskannya ke panggilan untuk
super()
. - Jika loader Anda memerlukan informasi tambahan untuk melakukan pemuatan, konstruktor bisa mengambil argumen tambahan. Dalam contoh yang ditampilkan di bawah ini, konstruktor menggunakan sebuah istilah kueri.
public StringListLoader(Context context, String queryString) {
super(context);
mQueryString = queryString;
}
Untuk melakukan pemuatan, gunakan metode penggantian loadInBackground()
, akibat metode doInBackground() dari AsyncTask
. Misalnya:
@Override
public List<String> loadInBackground() {
List<String> data = new ArrayList<String>;
//TODO: Load the data from the network or from a database
return data;
}
Mengimplementasikan callback
Gunakan konstruktor di callback onCreateLoader()
LoaderManager
, yang merupakan tempat membuat loader baru. Misalnya, callback onCreateLoader()
ini menggunakan konstruktor StringListLoader
yang didefinisikan di atas:
@Override
public Loader<List<String>> onCreateLoader(int id, Bundle args) {
return new StringListLoader(this, args.getString("queryString"));
}
Hasil loadInBackground()
secara otomatis diteruskan ke dalam callback onLoadFinished()
, di sinilah Anda bisa menampilkan hasil di UI. Misalnya:
public void onLoadFinished(Loader<List<String>> loader, List<String> data) {
mAdapter.setData(data);
}
Callback onLoaderReset()
hanya dipanggil bila loader akan dimusnahkan, sehingga seringkali Anda bisa mengosongkan onLoaderReset()
, karena Anda tidak akan mencoba mengakses data setelah loader ini dimusnahkan.
Bila Anda menggunakan AsyncTaskLoader
, data Anda akan bertahan bila ada perubahan konfigurasi perangkat. Jika aktivitas Anda dmusnahkan secara permanen, loader ini akan dimusnahkan bersamanya, tanpa tugas yang menanti dan mengonsumsi sumber daya sistem.
Loader juga memiliki manfaat lain, misalnya loader bisa memantau perubahan sumber data dan memuat ulang data jika terjadi perubahan. Anda akan mengetahui selengkapnya tentang loader tertentu di pelajaran berikutnya.
Praktik terkait
Latihan terkait dan dokumentasi praktik ada di Dasar-Dasar Developer Android: Praktik.